package tuioZones;

import java.util.ArrayList;

import processing.core.PApplet;

public class TouchZone implements Draggable, Rotatable, Comparable<TouchZone>{
	private double width,height;
	private PointXY coord;
	private ArrayList<TouchPoint> touchPoints;
	private float angle;
	private Card card;
	private Puzzle puzzle;
	private long lastAction;
	
	private PointXY rotationPoint = null;

	public TouchZone(PApplet myApplet, float xIn, float yIn, float width, float height) {
		this.coord = new PointXY(xIn,yIn);
		this.height = height;
		this.width = width;
		this.touchPoints = new ArrayList<TouchPoint>();
		this.angle = (float)(Math.random() * 360);
	}
	
	/**
	 * @return the x
	 */
	public float getX() {
		return coord.getX();
	}

	/**
	 * @param x the x to set
	 */
	public void setX(float x) {
		this.coord.setX(x);
	}

	/**
	 * @return the y
	 */
	public float getY() {
		return coord.getY();
	}

	/**
	 * @param y the y to set
	 */
	public void setY(float y) {
		this.coord.setY(y);
	}

	/**
	 * @return the width
	 */
	public double getWidth() {
		return width;
	}

	/**
	 * @param width the width to set
	 */
	public void setWidth(float width) {
		this.width = width;
	}

	/**
	 * @return the height
	 */
	public double getHeight() {
		return height;
	}

	/**
	 * @param height the height to set
	 */
	public void setHeight(double height) {
		this.height = height;
	}

	public PointXY getTranslationPoint() {
		return coord;
	}
	
	public void rotate(float newangle) {
		setAngle(newangle + getAngle());
	}
	
	public boolean contains(TouchPoint tp) {
		
		return contains(tp.getLastPoint());
	}
	
	public boolean contains(PointXY xy) {
		PointXY a = getCoordA();
		PointXY b = getCoordB();
		PointXY c = getCoordC();
		PointXY d = getCoordD();
		try {
			double distanceToAB = distanceToLine(a,b,xy);
			double distanceToBC = distanceToLine(b,c,xy);
			double distanceToCD = distanceToLine(c,d,xy);
			double distanceToDA = distanceToLine(d,a,xy);

			return distanceToAB <= getCoordB().distanceTo(getCoordC()) && distanceToBC <= getCoordC().distanceTo(getCoordD()) && distanceToCD <= getCoordD().distanceTo(getCoordA()) && distanceToDA <= getCoordA().distanceTo(getCoordB());
		}catch (Exception e) {
			float x1 = Math.min(Math.min(a.getX(), b.getX()),Math.min(c.getX(),d.getX()));
			float y1 = Math.min(Math.min(a.getY(), b.getY()),Math.min(c.getY(),d.getY()));
			float x2 = Math.max(Math.max(a.getX(), b.getX()),Math.max(c.getX(),d.getX()));
			float y2 = Math.max(Math.max(a.getY(), b.getY()),Math.max(c.getY(),d.getY()));
			
			return x1 <= xy.getX() && x2 >= xy.getX() && y1 <= xy.getY() && y2 >= xy.getY();
		}
	}
	
	/*
	 *   A        B
	 *   +--------+
	 *   |        |
	 *   |        |
	 *   +--------+
	 *   D        C
	 * 
	 */
	
	
	/**
	 * Hessesche Normalform
	 * 
	 * y = k*x + d
	 * 0 = k*x + d - y
	 * HNF:
	 * dist = k*push.getX() + d - push.getY()
	 */
	public double distanceToLine(PointXY p1, PointXY p2, PointXY push) throws Exception {
		double kx = p2.getX() - p1.getX();
		double ky = p2.getY() - p1.getY();
		
		double k = ky / kx;
		
		if(Double.isNaN(k) || Double.isInfinite(k))
			throw new Exception();
		
		double d = p1.getY() - p1.getX() * k;
		
		double div = Math.sqrt(Math.pow(k,2) + 1);
		
		double dist = (k * push.getX() + d - push.getY()) / div;
		
		return Math.abs(dist);
	}

	public PointXY getCoordA() {
		return coord;
	}
	/**
	 * @return
	 */
	public PointXY getCoordB() {
		return new PointXY((float)(coord.getX() + Math.cos(Math.toRadians(angle)) * width), (float)(coord.getY() + Math.sin(Math.toRadians(angle)) * width));
	}
	
	/**
	 * @return
	 */
	public PointXY getCoordC() {
		return new PointXY((float)(getCoordB().getX() + Math.cos(Math.toRadians(angle + 90)) * height), (float)(getCoordB().getY() + Math.sin(Math.toRadians(angle + 90)) * height));
	}
	
	/**
	 * @return
	 */
	public PointXY getCoordD() {
		return new PointXY((float)(coord.getX() + Math.cos(Math.toRadians(angle + 90)) * height), (float)(coord.getY() + Math.sin(Math.toRadians(angle + 90)) * height));
	}

	public void addTouchPoint(TouchPoint tp) {
		if(!touchPoints.contains(tp))
			touchPoints.add(tp);

		actionDone();
	}
	
	public void removeTouchPoint(TouchPoint tp) {
		touchPoints.remove(tp);
	}

	public void flushTouchPoints() {
		touchPoints = new ArrayList<TouchPoint>();
	}

	/**
	 * @return the card
	 */
	public Card getCard() {
		return card;
	}

	/**
	 * @param card the card to set
	 */
	public void setCard(Card card) {
		this.puzzle = null;
		this.card = card;
	}

	/**
	 * @return the puzzle
	 */
	public Puzzle getPuzzle() {
		return puzzle;
	}

	/**
	 * @param puzzle the puzzle to set
	 */
	public void setPuzzle(Puzzle puzzle) {
		//this.card = null;
		this.puzzle = puzzle;
	}

	/**
	 * @return the angle
	 */
	public float getAngle() {
		return angle;
	}

	/**
	 * @param angle the angle to set
	 */
	public void setAngle(float angle) {
		this.angle = angle;
		if(this.angle > 360)
			this.angle -= 360;
		if(this.angle < 0)
			this.angle += 360;
	}

	/**
	 * @return the touchPoints
	 */
	public ArrayList<TouchPoint> getTouchPoints() {
		return touchPoints;
	}

	/**
	 * @return the primary
	 */
	public TouchPoint getPrimary() {
		if(touchPoints.size() == 0)
			return null;
		return touchPoints.get(0);
	}
	
	private void actionDone() {
		lastAction = System.currentTimeMillis();
	}
	
	public long getLastAction() {
		return lastAction;
	}

	public int compareTo(TouchZone o) {
		return (int)(getLastAction() - o.getLastAction());
	}
	
	public String toString() {
		return getCoordA() + " " + getAngle();
	}

	public void setRotationPoint(PointXY rot2) {
		this.rotationPoint = rot2;
	}

	/**
	 * @return the rotationPoint
	 */
	public PointXY getRotationPoint() {
		return rotationPoint;
	}
}
